package db

import (
	"chainproxy/config"
	"chainproxy/log"
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"time"
)

var mysqldb *gorm.DB

type PackageProxyRecord struct {
	gorm.Model
	PackageName string `gorm:"column:name;unique;not null"`		//包名
	PackagePath string `gorm:"column:path;not null"`
	VersionList string	`gorm:"column:versions"`				//包版本列表
	LatestVersion string `gorm:"column:latest"`
	Versions []VersionInfo `gorm:"ForeignKey:PackageId;"`
}

type VersionInfo struct {
	gorm.Model
	PackageId uint `gorm:"not null;unique_index:idx_pv"`
	Version string `gorm:"not null;unique_index:idx_pv"`
	CommitTime time.Time `gorm:"not null"`
}

type ErrorRecord struct {
	gorm.Model
	Url string `gorm:"type:varchar(300);not null"`		//请求的URL
	StatusCode int								//请求结果状态码
	Content string `gorm:"type:varchar(3000);not null"`	//请求错误的内容
}

type Upstream struct {
	gorm.Model
	Addr string	`gorm:"not null" json:"addr"`
	Avg_TTLB float64 `gorm:"avg_ttlb" json:"avg_ttlb"`		//平均响应时间
	AvgEts int `gorm:"avg_ets" json:"avg_ets"`			//平均错误次数
	Us []UpstreamStatis `gorm:"ForeignKey:UpstreamId;" json:"us"`
}

type UpstreamStatis struct {
	gorm.Model
	UpstreamId uint	`json:"u_id"`
	Type string	`gorm:"not null" json:"type"`	//接口类型
	AvgTTLB float64 `gorm:"avg_ttlb" json:"avg_ttlb"`
	AvgEts float64 `gorm:"avg_ets" json:"avg_ets"`
	ReqTs int `json:"req_ts"`		//请求次数
	TTLB float64 `json:"ttlb"`	//请求响应时间
	Ets int	`json:"ets"`		//错误次数
}

type MyDbLogger struct {
}

func (p *PackageProxyRecord)TableName() string {
	return "proxy_record"
}

func (e *ErrorRecord)TableName() string {
	return "error_record"
}

func (v *VersionInfo)TableName() string {
	return "version_info"
}

func InitDb()  {
	//root:123456@tcp(localhost:3307)/testdb?charset=utf8&parseTime=True&loc=Local
	dialect := config.GetString("db.dialect")
	var cts string
	if dialect == "mysql"{
		name := config.GetString("db.name")
		user := config.GetString("db.user")
		pwd := config.GetString("db.pwd")
		protocol := config.GetString("db.protocol")
		host := config.GetString("db.host")
		port := config.GetInt("db.port")
		params := config.GetString("db.params")
		cts = fmt.Sprintf("%s:%s@%s(%s:%d)/%s?%s", user, pwd, protocol, host, port, name, params)
	}else if dialect == "sqlite3" {
		cts = config.GetString("sqlite3.db")
	}

	log.Logger.Infof("db connect string: %s", cts)
	db, err := gorm.Open(dialect, cts)
	if err != nil{
		fmt.Println(err)
		panic(err)
	}
	db.LogMode(true)
	db.SetLogger(&MyDbLogger{})

	mysqldb = db
	//自动创建或更新表
	var p PackageProxyRecord
	var e ErrorRecord
	var v VersionInfo
	var u Upstream
	var us UpstreamStatis
	if config.GetBool("db.deletable"){
		db.DropTableIfExists(&p, &v, &e, &u, &us)
	}
	db.AutoMigrate(&p, &e, &v, &u, &us)
	db.Model(&p).Related(&v)
	db.Model(&u).Related(&us)
}

func (l MyDbLogger)Print(values ...interface{}){
	log.Logger.ReportCaller = false
	log.Logger.Print(gorm.LogFormatter(values...)...)
	log.Logger.ReportCaller = true
}

func Insert(value interface{}) error {
	err := mysqldb.Create(value).Error
	return err
}

func FindOne(out interface{}, query interface{}, args ...interface{}) bool {
	result := mysqldb.Where(query, args...).First(out)
	if result.RecordNotFound() {
		return false
	}
	return true
}

func FindOneWithPreloadVersions(out interface{}, query interface{}, args ...interface{}) (bool, error) {
	return FindOneWithPreload(out, "Versions", query, args...)
}

func FindOneWithPreloadUs(out interface{}, query interface{}, args ...interface{}) (bool, error) {
	return FindOneWithPreload(out, "Us", query, args...)
}

func FindOneWithPreload(out interface{}, clumn string, query interface{}, args ...interface{}) (bool, error) {
	result := mysqldb.Where(query, args...).Preload("Versions").First(out)
	if result.RecordNotFound() {
		return false, nil
	}
	return true, result.Error
}

func UpdateAll(value interface{}) error {
	return mysqldb.Save(value).Error
}

func FindAllWithPreloadUs(out interface{}) error {
	result := mysqldb.Preload("Us").Find(out)
	if result.RecordNotFound() {
		return nil
	}
	return result.Error
}

func Delete(value interface{}) error {
	return mysqldb.Unscoped().Delete(value).Error
}

func DeleteUpstreamWithRelate(u *Upstream) error {
	tx := mysqldb.Begin()
	uId := u.ID
	err := mysqldb.Unscoped().Delete(u).Error
	if err != nil{
		tx.Rollback()
		return err
	}
	if len(u.Us) == 0{
		tx.Commit()
		return nil
	}
	err = mysqldb.Unscoped().Where("upstream_id = ?", uId).Delete(&UpstreamStatis{}).Error
	if err != nil{
		tx.Rollback()
		return err
	}
	tx.Commit()
	return nil
}